Chapter 4 ASSIGNMENT & LOGICAL COMPARES Throughout this chapter, references are given to various ranges of variables. Your compiler may use a different range for some of the variables since the proposed ANSI standard does not define specific limits for all data types. Consult the documentation for your compiler for the exact range for each of the variable types. INTEGER ASSIGNMENT STATEMENTS ______________________________________________________________ Load the file INTASIGN.C and display it for ============== an example of assignment statements. Three INTASIGN.C variables are defined for use in the program ============== and the rest of the program is merely a series of illustrations of various assignments. The first two lines of the assignment statements assign numerical values to "a" and "b", and the next five lines illustrate the five basic arithmetic functions and how to use them. The fifth is the modulo operator and gives the remainder if the two variables were divided. It can only be applied to "int" or "char" type variables. Following these, there are two lines illustrating how to combine some of the variables in some complex math expressions. All of the above examples should require no comment except to say that none of the equations are meant to be particularly useful except as illustrations. The "char" type variable will be defined in the description of the next example program. The expressions in lines 17 and 18 are perfectly acceptable as given, but we will see later in this chapter that there is another way to write these for more compact code. VERY STRANGE LOOKING CODE ______________________________________________________________ This leaves us with the last two lines which may appear to you as being very strange. The C compiler scans the assignment statement from right to left, (which may seem a bit odd since we do not read that way), resulting in a very useful construct, namely the one given here. The compiler finds the value 20, assigns it to "c", then continues to the left finding that the latest result of a calculation should be assigned to "b". Thinking that the latest calculation resulted in a 20, it assigns it to "b" also, and continues the leftward scan assigning the value 20 to "a" also. This is a very useful construct when you are initializing a group of variables. The last statement illustrates that it is possible to actually do some calculations to arrive at the value which 4-1 Chapter 4 - Assignment & Logical Compares will be assigned to all three variables. In fact, the rightmost expression can contain variables, even "a", "b", & "c". The program has no output, so compiling and executing this program will be very uninteresting. Since you have already learned how to display some integer results using the "printf" function, it would be to your advantage to add some output statements to this program to see if the various statements do what you think they should do. DEFINITIONS FIRST THEN EXECUTABLE STATEMENTS ______________________________________________________________ This would be a good time for a preliminary definition of a rule to be followed in C. The data definitions are always given before any executable statements in any program block. This is why the variables are defined first in this program and in every C program. If you try to define a new variable after executing some statements, your compiler will issue an error. ADDITIONAL DATA TYPES ______________________________________________________________ Loading and editing MORTYPES.C will ============== illustrate how some additional data types can MORTYPES.C be used. Once again we have defined a few ============== integer type variables which you should be fairly familiar with by now, but we have added two new types, the "char", and the "float". The "char" type of data is nearly the same as the integer except that it can only be assigned numerical values between -128 to 127 on most implementations of C, since it is stored in only one byte of memory. The "char" type of data is usually used for ASCII data, more commonly known as text. The text you are reading was originally written on a computer with a word processor that stored the words in the computer one character per byte. In contrast, the integer data type is stored in two bytes of computer memory on nearly all microcomputers. DATA TYPE MIXING ______________________________________________________________ It would be profitable at this time to discuss the way C handles the two types "char" and "int". Most functions in C that are designed to operate with integer type variables will work equally well with character type variables because they are a form of an integer variable. Those functions, when 4-2 Chapter 4 - Assignment & Logical Compares called on to use a "char" type variable, will actually promote the "char" data into integer data before using it. For this reason, it is possible to mix "char" and "int" type variables in nearly any way you desire. The compiler will not get confused, but you might. It is good not to rely on this too much, but to carefully use only the proper types of data where they should be used. The second new data type is the "float" type of data, commonly called floating point data. This is a data type which usually has a very large range, a large number of significant digits, and a large number of computer words are required to store it. The "float" data type has a decimal point associated with it and has an allowable range of from 3.4E-38 to 3.4E+38 when using most C compilers on microcomputers, and is composed of about 7 significant digits. HOW TO USE THE NEW DATA TYPES ______________________________________________________________ The first three lines of the program assign values to all nine of the defined variables so we can manipulate some of the data between the different types. Since, as mentioned above, a "char" data type is in reality an "integer" data type, no special considerations need be taken to promote a "char" to an "int", and a "char" type data field can be assigned to an "int" variable. When going the other way, there is no standard, so you may simply get garbage if the value of the integer variable is outside the range of the "char" type variable. Most C compilers simply truncate the most significant bits and use the 8 least significant bits. It will translate correctly if the value is within the range of -128 to 127. The third line illustrates the simplicity of translating an integer into a "float", simply assign it the new value and the system will do the proper conversion. When going the other way however, there is an added complication. Since there may be a fractional part of the floating point number, the system must decide what to do with it. By definition, it will truncate it. This program produces no output, and we haven't covered a way to print out "char" and "float" type variables, so you can't really get in to this program and play with the results, but the next program will cover this for you. Be sure to compile and run this program after you are sure you understand it completely. 4-3 Chapter 4 - Assignment & Logical Compares LOTS OF VARIABLE TYPES ______________________________________________________________ Load the file LOTTYPES.C and display it on ============== your screen. This file contains nearly every LOTTYPES.C standard simple data type available in the ============== programming language C. There are other types, but they are the compound types (ie - arrays and structures) that we will cover in due time. Observe the file. First we define a simple "int", followed by a "long int" which has a range of -2147483648 to 2147483647 with most C compilers, and a "short int" which has a range that is identical to that for the "int" variable, namely -32768 to 32767. The "unsigned" is next and is defined as the same size as the "int" but with no sign. The "unsigned" then will cover a range of 0 to 65535. It should be pointed out that when the "long", "short", or "unsigned" is desired, the "int" is optional and is left out by most experienced programmers. We have already covered the "char" and the "float", which leaves only the "double". The "double" covers a greater range than the "float" and has more significant digits for more precise calculations. It also requires more memory to store a value than the simple "float". The "double" in most C compilers covers a range of 1.7E-308 to 1.7E+308. Note that other compounding of types can be done such as "long unsigned int", "unsigned char", etc. Check your documentation for a complete list of variable types. Another diversion is in order at this point. Your compiler has no provisions for floating point math, but only double floating point math. It will promote a "float" to a "double" before doing calculations and therefore only one math library will be needed. Of course, this is totally transparent to you, so you don't need to worry about it. Because of this, you may think that it would be best to simply define every floating point variable as double, since they are promoted before use in any calculations, but that may not be a good idea. A "float" variable requires 4 bytes of storage and a "double" requires 8 bytes of storage, so if you have a large volume of floating point data to store, the "double" will obviously require much more memory. After defining the data types in the program under consideration, a numerical value is assigned to each of the defined variables in order to demonstrate the means of outputting each to the monitor. 4-4 Chapter 4 - Assignment & Logical Compares SOME LATE ADDITIONS ______________________________________________________________ As any programming language evolves, additional constructs are added to fill some previously overlooked need. Two new keywords have been added to C recently, and since they may not be a part of your compiler, they are not illustrated in example programs, but they will be discussed here. The two new keywords are "const" and "volatile" and are used to tell the compiler that variables of these types are constants and cannot be changed by the program. A constant declared with the const keyword declares a value that will never be changed by either the program or the system itself. If volatile is used, it declares a value that will never be changed by the program but may be changed by some outside influence such as a clock update pulse incrementing a volatile type stored value. Since these constants can never have a value assigned to them in the executable part of the program, they should always be initialized. Examples of use in declaring constants of these two types are given as; const int index1 = 2; const index2 = 6; volatile const int index3 = 12; volatile index4 = -23; As mentioned earlier in this chapter, the keyword "int" is optional when used with these constant definitions THE CONVERSION CHARACTERS ______________________________________________________________ Following is a list of some of the conversion characters and the way they are used in the "printf" statement. A complete list of all of the conversion characters should be included with the documentation for your compiler. d decimal notation o octal notation x hexadecimal notation u unsigned notation c character notation s string notation f floating point notation Each of these is used following a percent sign to indicate the type of output conversion, and between those two characters, the following fields may be added. 4-5 Chapter 4 - Assignment & Logical Compares - left justification in its field (n) a number specifying minimum field width . to separate n from m (m) significant fractional digits for a float l to indicate a "long" These are all used in the examples which are included in the program presently displayed on your monitor, with the exception of the string notation which will be covered later in this tutorial. Note especially the variable field width specification demonstrated in lines 33 to 36. This is not part of the original definition of C, but it is included in the proposed ANSI standard and will become part of the C language. Compile and run this program to see what effect the various fields have on the output. You now have the ability to display any of the data fields in the previous programs and it would be to your advantage to go back and see if you can display some of the fields anyway you desire. LOGICAL COMPARES ______________________________________________________________ Load and view the file named COMPARES.C for ============== many examples of compare statements in C. We COMPARES.C begin by defining and initializing nine ============== variables to use in the following compare statements. This initialization is new to you and can be used to initialize variables while they are defined. The first group of compare statements represents the simplest kinds of compares since they simply compare two variables. Either variable could be replaced with a constant and still be a valid compare, but two variables is the general case. The first compare checks to see if "x" is equal to "y" and it uses the double equal sign for the comparison. A single equal sign could be used here but it would have a different meaning as we will see shortly. The second comparison checks to see if "x" is greater than "z". The third compare introduces the "NOT" operator, the exclamation, which can be used to invert the result of any logical compare. The fourth checks for "b" less than or equal to "c", and the last checks for "r" not equal to "s". As we learned in the last chapter, if the result of the compare is true, the statement following the "if" clause will be executed and the results are given in the comments. Note that "less than" and "greater than or equal to" are also available, but are not illustrated here. 4-6 Chapter 4 - Assignment & Logical Compares It would be well to mention the different format used for the "if" statement in this example program. A carriage return is not required as a statement separator and by putting the conditional clause on the same line as the "if", it adds to the readability of the overall program. MORE COMPARES ______________________________________________________________ The compares in the second group are a bit more involved. Starting with the first compare, we find a rather strange looking set of conditions in the parentheses. To understand this we must understand just what a "true" or "false" is in the C language. A "false" is defined as a value of zero, and "true" is defined as a non-zero value. Any integer or char type of variable can be used for the result of a true/false test, or the result can be an implied integer or char. Look at the first compare of the second group of compare statements. The expression "r != s" will evaluate as a "true" since "r" was set to 0.0 above, so the result will be a non-zero value. With most C compilers, it would always be set to a 1, but you could get in trouble if you wrote a program that depended on it being 1 in all cases. Good programming practice would be to not use the resulting 1 in any calculations. Even though the two variables that are compared are "float" variables, the result will be of type "integer". There is no explicit variable to which it will be assigned so the result of the compare is an implied integer. Finally the resulting number, probably 1 in this case, is assigned to the integer variable "x". If double equal signs were used, the phantom value, namely 1, would be compared to the value of "x", but since the single equal sign is used, the value 1 is simply assigned to "x", as though the statement were not in parentheses. Finally, since the result of the assignment in the parentheses was non-zero, the entire expression is evaluated as "true", and "z" is assigned the value of 1000. Thus we accomplished two things in this statement, we assigned "x" a new value, probably 1, and we assigned "z" the value of 1000. We covered a lot in this statement so you may wish to review it before going on. The important things to remember are the values that define "true" and "false", and the fact that several things can be assigned in a conditional statement. The value assigned to "x" was probably a 1, but remember that the only requirement is that it is nonzero. The next example should help clear up some of the above in your mind. In this example, "x" is assigned the value of "y", and since the result is 11, the condition is non-zero, which is true, and the variable "z" is assigned 222. The third example, in the second group, compares "x" to zero. If the result is true, meaning that if "x" is not zero, then 4-7 Chapter 4 - Assignment & Logical Compares "z" is assigned the value of 333, which it will be. The last example in this group illustrates the same concept, since the result will be true if "x" is non-zero. The compare to zero is not actually needed and the result of the compare is true. The third and fourth examples of this group are therefore identical. ADDITIONAL COMPARE CONCEPTS ______________________________________________________________ The third group of compares will introduce some additional concepts, namely the logical "AND" and the logical "OR". We assign the value of 77 to the three integer variables simply to get started again with some defined values. The first compare of the third group contains the new control "&&", which is the logical "AND". The entire statement reads, if "x" equals "y" AND if "x" equals 77 then the result is "true". Since this is true, the variable z is set equal to 333. The next compare in this group introduces the "||" operator which is the "OR". The statement reads, if "x" is greater than "y" OR if "z" is greater than 12 then the result is true. Since "z" is greater than 12, it doesn't matter if "x" is greater than "y" or not, because only one of the two conditions must be true for the result to be true. The result is true, so therefore "z" will be assigned the value of 22. LOGICAL EVALUATION ______________________________________________________________ When a compound expression is evaluated, the evaluation proceeds from left to right and as soon as the result of the outcome is assured, evaluation stops. Namely, in the case of an "AND" evaluation, when one of the terms evaluates to "false", evaluation is discontinued because additional true terms cannot make the result ever become "true". In the case of an "OR" evaluation, if any of the terms is found to be "true", evaluation stops because it will be impossible for additional terms to cause the result to be "false". In the case of additionally nested terms, the above rules will be applied to each of the nested levels. PRECEDENCE OF OPERATORS ______________________________________________________________ The question will come up concerning the precedence of operators. Which operators are evaluated first and which last? There are many rules about this topic, but I would suggest that you don't worry about it at this point. Instead, use lots of parentheses to group variables, constants, and operators in a way meaningful to you. Parentheses always have 4-8 Chapter 4 - Assignment & Logical Compares the highest priority and will remove any question of which operations will be done first in any particular statements. Going on to the next example in group three, we find three simple variables used in the conditional part of the compare. Since all three are non-zero, all three are "true", and therefore the "AND" of the three variables are true, leading to the result being "true", and "z" being assigned the value of 11. Note that since the variables, "r", "s", and "t" are "float" type variables, they could not be used this way, but they could each be compared to zero and the same type of expression could be used. Continuing on to the fourth example of the third group we find three assignment statements in the compare part of the "if" statement. If you understood the above discussion, you should have no difficulty understanding that the three variables are assigned their respective new values, and the result of all three are non-zero, leading to a resulting value of "TRUE". THIS IS A TRICK, BE CAREFUL ______________________________________________________________ The last example of the third group contains a bit of a trick, but since we have covered it above, it is nothing new to you. Notice that the first part of the compare evaluates to "FALSE". The remaining parts of the compare are not evaluated, because it is an "AND" and it will definitely be resolved as a "FALSE" because the first term is false. If the program was dependent on the value of "y" being set to 3 in the next part of the compare, it will fail because evaluation will cease following the "FALSE" found in the first term. Likewise, "z" will not be set to 4, and the variable "r" will not be changed. POTENTIAL PROBLEM AREAS ______________________________________________________________ The last group of compares illustrate three possibilities for getting into a bit of trouble. All three have the common result that "z" will not get set to the desired value, but for different reasons. In the case of the first one, the compare evaluates as "true", but the semicolon following the second parentheses terminates the "if" clause, and the assignment statement involving "z" is always executed as the next statement. The "if" therefore has no effect because of the misplaced semicolon. The second statement is much more straightforward because "x" will always be equal to itself, therefore the inequality will never be true, and the entire statement will never do a thing, but is wasted effort. The last statement will always assign 0 to "x" and the compare 4-9 Chapter 4 - Assignment & Logical Compares will therefore always be "false", never executing the conditional part of the "if" statement. The conditional statement is extremely important and must be thoroughly understood to write efficient C programs. If any part of this discussion is unclear in your mind, restudy it until you are confident that you understand it thoroughly before proceeding onward. Compile and run this program. Add some printout to see the results of some of the operations. THE CRYPTIC PART OF C ______________________________________________________________ There are three constructs used in C that ============= make no sense at all when first encountered CRYPTIC.C because they are not intuitive, but they ============= greatly increase the efficiency of the compiled code and are used extensively by experienced C programmers. You should therefore be exposed to them and learn to use them because they will appear in most, if not all, of the programs you see in the publications. Load and examine the file named CRYPTIC.C for examples of the three new constructs. In this program, some variables are defined and initialized in the same statements for use below. The first executable statement simply adds 1 to the value of "x", and should come as no surprise to you. The next two statements also add one to the value of "x", but it is not intuitive that this is what happens. It is simply by definition that this is true. Therefore, by definition of the C language, a double plus sign either before or after a variable increments that variable by 1. Additionally, if the plus signs are before the variable, the variable is incremented before it is used, and if the plus signs are after the variable, the variable is used, then incremented. In the next statement, the value of "y" is assigned to the variable "z", then "y" is incremented because the plus signs are after the variable "y". In the last statement of the incrementing group of example statements, the value of "y" is incremented then its value is assigned to the variable "z". The next group of statements illustrate decrementing a variable by one. The definition works exactly the same way for decrementing as it does for incrementing. If the minus signs are before the variable, the variable is decremented, then used, and if the minus signs are after the variable, the variable is used, then decremented. 4-10 Chapter 4 - Assignment & Logical Compares THE CRYPTIC ARITHMETIC OPERATOR ______________________________________________________________ Another useful but cryptic operator is the arithmetic operator. This operator is used to modify any variable by some constant value. The first statement of the "arithmetic operator" group of statements simply adds 12 to the value of the variable "a". The second statement does the same, but once again, it is not intuitive that they are the same. Any of the four basic functions of arithmetic, "+", "-", "*", or "/", can be handled in this way, by putting the function desired in front of the equal sign and eliminating the second reference to the variable name. It should be noted that the expression on the right side of the arithmetic operator can be any valid expression, the examples are kept simple for your introduction to this new operator. Just like the incrementing and decrementing operators, the arithmetic operator is used extensively by experienced C programmers and it would pay you well to understand it. THE CONDITIONAL EXPRESSION ______________________________________________________________ The conditional expression is just as cryptic as the last two, but once again it can be very useful so it would pay you to understand it. It consists of three expressions within parentheses separated by a question mark and a colon. The expression prior to the question mark is evaluated to determine if it is "true" or "false". If it is true, the expression between the question mark and the colon is evaluated, and if it is not true, the expression following the colon is evaluated. The result of the evaluation is used for the assignment. The final result is identical to that of an "if" statement with an "else" clause. This is illustrated by the second example in this group. The conditional expression has the added advantage of more compact code that will compile to fewer machine instructions in the final program. The final two lines of this example program are given to illustrate a very compact way to assign the greater of two variables "a" or "b" to "c", and to assign the lessor of the same two variables to "c". Notice how efficient the code is in these two examples. TO BE CRYPTIC OR NOT TO BE CRYPTIC ______________________________________________________________ Several students of C have stated that they didn't like these three cryptic constructs and that they would simply never use them. This will be fine if they never have to read anybody else's program, or use any other programs within their own. 4-11 Chapter 4 - Assignment & Logical Compares I have found many functions that I wished to use within a program but needed a small modification to use it, requiring me to understand another person's code. It would therefore be to your advantage to learn these new constructs, and use them. They will be used in the remainder of this tutorial, so you will be constantly exposed to them. This has been a long chapter but it contained important material to get you started in using C. In the next chapter, we will go on to the building blocks of C, the functions. At that point, you will have enough of the basic materials to allow you to begin writing meaningful programs. PROGRAMMING EXERCISES ______________________________________________________________ 1. Write a program that will count from 1 to 12 and print the count, and its square, for each count. 1 1 2 4 3 9 etc. 2. Write a program that counts from 1 to 12 and prints the count and its inversion to 5 decimal places for each count. This will require a floating point number. 1 1.00000 2 .50000 3 .33333 4 .25000 etc. 3. Write a program that will count from 1 to 100 and print only those values between 32 and 39, one to a line. 4-12